#pragma once 
#include<unistd.h>

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include"Mutex.hpp"
#include"Cond.hpp"
#include"Thread.hpp"
#include"Logger.hpp"

//单例线程池  懒汉模式
const static int defaultthreadnum = 3;

template<typename T>
class ThreadPool
{
private:
    //判断任务队列是否为空
    bool QueueIsEmpty()
    {
        return _q.empty();
    }
    void Routine(const std::string& name)
    {   
        while(true)
        {
            //将任务从线程获取带线程私有
            T t;
            {
                //上锁
                LockGuard lockguard(&_lock);
                //任务队列为空，线程进行等待
                while(QueueIsEmpty() && _is_running)
                {
                    _wait_thread_num++;
                    _cond.Wait(_lock);
                    _wait_thread_num--;
                }
                //当线程退出、任务队列为空 --> 退出
                if(!_is_running && QueueIsEmpty())
                {
                    LOG(LogLevel::INFO) << "线程池退出 && 任务队列为空， " << name << "退出";
                    break;
                }

                //到这个地方就是说，任务队列中有任务
                //1、线程池退出 --消耗历史
                //2、线程池没有退出-- 正常工作
                 t  = _q.front();
                 _q.pop();
            }
            t();//执行任务
            //打印日志
            LOG(LogLevel::DEBUG) << name <<" handler task: " << t.Result2String();
        }
    }

    //构造
    ThreadPool(int threadnum = defaultthreadnum)
        :_threadnum(defaultthreadnum)
        ,_is_running(false)
        ,_wait_thread_num(0)
        {
            for(int i = 0;i<defaultthreadnum;i++)
            {
                //创建线程放入_q 中
                std::string name = "thread-" + std::to_string(i+1);
                _threads.emplace_back([this](const std::string& name)
                                            {this->Routine(name);} , name);
            }
            LOG(LogLevel::INFO) << "thread pool obj create success";
        }

        ThreadPool<T>& operator=(const ThreadPool<T>& ) = default;
        ThreadPool(const ThreadPool<T>& ) = default;

public:
    void Start()
    {
        if(_is_running) return;
        _is_running = true;
        for(auto& t : _threads)
        {
            t.Start();
        }
        LOG(LogLevel::INFO) << "thread pool running success";
    }

    //让线程走正常的唤醒逻辑退出
    //当线程池要退出
    //1、如果被唤醒 && 任务队列没有任务 = 让线程退出
    //2、如果被唤醒 && 任务队列有任务 = 线程不能立即退出，应该先将任务执行完然后再退出
    //3、线程本身没有被休眠，我们应该让他将能处理的任务处理完然后再退出
    //如果队列中有任务，线程不会休眠
    void Stop()
    {
        if(!_is_running) return;
        _is_running = false;
        if(_wait_thread_num) _cond.NotifyAll();
    }

    //等待
    void Wait()
    {
        //遍历所有的线程然后join
        for(auto& t : _threads)
        {
            t.Join();
        }
        LOG(LogLevel::INFO) << "thread pool wait success";
    }
    //将任务加入任务队列中
    void Enqueue(const T &t)
    {
        //如果线程没有运行就不加入任务队列
        if(!_is_running) return;
        {
            LockGuard lockguard(&_lock);

            _q.push(t);
            //有了任务就可以唤醒一个等待的线程
            if(_wait_thread_num > 0) _cond.NotifyOne();
        }
    }
    //用来测试的一个函数
    static std::string ToHex(ThreadPool<T>* addr)
    {
        char buffer[64];
        //将
        snprintf(buffer , sizeof(buffer) , "%p" , addr);
        return buffer;
    }
    static ThreadPool<T>* GetInstance()//获取单例
    {
        {
            if(!_instance)//如果没有创建就进来
            {
                //这样处理更加高效
                LockGuard lockguard(&_singleton_lock);//竞争锁
                if(!_instance)//没有创建线程池才会创建
                {
                    _instance = new ThreadPool<T>();
                    LOG(LogLevel::DEBUG) << "线程池首次被使用, 创建并初始化, addr: " << ToHex(_instance);

                    _instance->Start();//创建线程池
                }
            }
        }
        return _instance;
    }
    ~ThreadPool()
    {}
private:
    //任务队列
    std::queue<T> _q;//整体使用的临界资源

    //多个线程
    std::vector<Thread> _threads;//1、创建线程对象 2、让线程对象启动
    int _threadnum;//线程数量
    int _wait_thread_num;//等待的线程数量

    //保护机制
    Mutex _lock;//锁
    Cond _cond;//条件变量

    //其他属性
    bool _is_running;//线程池是否运行

    //单例中静态指针
    static ThreadPool<T> *_instance;
    static Mutex _singleton_lock;
};

//静态成员变量只能再全局初始化
template<class T>
ThreadPool<T>* ThreadPool<T>::_instance = nullptr;

template<class T>
Mutex ThreadPool<T>::_singleton_lock;
